/*
 * Copyright 2023 Shenzhen Kaihong DID Co., Ltd..
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <stdint.h>
#include <pthread.h>

#include "vendorGnssAdapter.h"

#define GNSSMGT "/vendor/soc_platform/lib64/libgnssmgt.z.so"
#define APN_NAME "CMNET"

using namespace OHOS::Location;
using namespace OHOS::HDI::Location;

AGpsCallbacks sAGpsCallbacks = {agnss_status_callback,
                                agnss_create_thread_cb};

AGpsRilCallbacks sAGpsRilCallbacks = {  agnss_ril_request_set_id_callback,
                                        agnss_ril_request_ref_loc_callback,
                                        agnss_create_thread_cb
                                     };

int gnss_enable(GnssCallbackStruct *callbacks);
int gnss_disable(void);
int gnss_start(uint32_t type);
int gnss_stop(uint32_t type);
int gnss_injects_reference_information(int type, GnssReferenceInfo *info);
int gnss_set_gnss_config_para(GnssConfigParameter *para);
void gnss_remove_auxiliary_data(uint16_t flags);
int gnss_inject_extended_ephemeris(char *data, int length);
int gnss_get_cached_locations_size();
void gnss_flush_cached_gnss_locations();
const void *get_gnss_module_iface(int iface);

void aGnssStatusChange(const AgnssDataConnectionRequest *status);
void aGnssgetSetid(uint16_t type);
void aGnssgetRefLoc(uint32_t type);

bool aGnssSetCallback(AgnssCallbackIfaces *callbacks);
bool aGnssSetRefLocation(const AgnssReferenceInfo *refLoc);
bool aGnssSetSetid(uint16_t type, const char *setid, size_t len);
bool aGnssSetAgnssServer(uint16_t type, const char *server, size_t len,
                         int32_t port);
/*ohos function*/
//const void* get_module_iface(int iface);
const GnssVendorInterface* get_gnss_interface();
const AgnssModuleInterface* get_agnss_interface();

// so vendorinface entry
GnssVendorDevice syml_GnssVendorInterface = {sizeof(GnssVendorDevice),
                                             get_gnss_interface};
GnssVendorInterface g_GVI_ = {
    sizeof(GnssVendorInterface),
    gnss_enable,
    gnss_disable,
    gnss_start,
    gnss_stop,
    gnss_injects_reference_information,
    gnss_set_gnss_config_para,
    gnss_remove_auxiliary_data,
    gnss_inject_extended_ephemeris,
    gnss_get_cached_locations_size,
    gnss_flush_cached_gnss_locations,
    get_gnss_module_iface // get_module_iface
};

AgnssModuleInterface g_AGMI_ = {sizeof(AgnssModuleInterface), aGnssSetCallback,
                              aGnssSetRefLocation, aGnssSetSetid,
                              aGnssSetAgnssServer};

void *g_Handle = NULL;
GnssCallbackStruct g_GCS_={0};
int gnss_enable(GnssCallbackStruct *callbacks) {
  LBSLOGE(GNSS, "%{public}s entered", GNSSMGT);
  if (callbacks == nullptr){
    LBSLOGE(GNSS, "%{public}s callbacks == nullptr return", GNSSMGT);
  }

  DL_RET ret=SO_OK;
  g_GCS_ = *callbacks;
  g_Handle = dlopen(GNSSMGT, RTLD_LAZY);
  LBSLOGE(GNSS, "%{public}s GNSSMGT handle addr is %x", GNSSMGT, g_Handle);

  if (g_Handle == NULL) 
  {
    LBSLOGE(GNSS, "%{public}s load failed", GNSSMGT);
    return LOAD_NOK;
  }

  gps_init = (pGnssmgt_init)dlsym(g_Handle, "gnssmgt_init");
  LBSLOGE(GNSS, "%{public}s gps_init addr is %{public}x", GNSSMGT, gps_init);

  gps_start = (pGnssmgt_start)dlsym(g_Handle, "gnssmgt_start");
  LBSLOGE(GNSS, "%{public}s gps_start addr is %{public}x", GNSSMGT, gps_start);

  gps_stop = (pGnssmgt_stop)dlsym(g_Handle, "gnssmgt_stop");
  LBSLOGE(GNSS, "%{public}s gps_stop addr is %{public}x", GNSSMGT, gps_stop);

  gps_cleanup = (pGnssmgt_cleanup)dlsym(g_Handle, "gnssmgt_cleanup");
  LBSLOGE(GNSS, "%{public}s gps_cleanup addr is %{public}x", GNSSMGT, gps_cleanup);

  agps_init = (pAGnssmgt_init)dlsym(g_Handle, "gnssmgt_agps_init");
  LBSLOGE(GNSS, "%{public}s agps_init addr is %{public}x", GNSSMGT, agps_init);

  agps_setPosMode = (pAGnssmgt_setPosMode)dlsym(g_Handle, "gnssmgt_setPosMode");
  LBSLOGE(GNSS, "%{public}s agps_setPosMode addr is %{public}x", GNSSMGT, agps_setPosMode);

  agps_set_server = (pAGnssmgt_set_server)dlsym(g_Handle, "gnssmgt_agps_setServer");
  LBSLOGE(GNSS, "%{public}s agps_set_server addr is %{public}x", GNSSMGT, agps_set_server);

  agps_open_conn = (pAGnssmgt_open_conn)dlsym(g_Handle, "gnssmgt_agps_openConn");
  LBSLOGE(GNSS, "%{public}s agps_open_conn addr is %{public}x", GNSSMGT, agps_open_conn);

  agps_close_conn= (pAGnssmgt_agps_closeConn)dlsym(g_Handle, "gnssmgt_agps_closeConn");
  LBSLOGE(GNSS_TEST, "%{public}s agps_close_conn addr is %{public}x", GNSSMGT, agps_close_conn);

  agps_open_failed= (pAGnssmgt_agps_openFailed)dlsym(g_Handle, "gnssmgt_agps_openFailed");
  LBSLOGE(GNSS_TEST, "%{public}s agps_open_failed addr is %{public}x", GNSSMGT, agps_open_failed);

  agps_open_with_apn= (pAGnssmgt_agps_openWithApnIpType)dlsym(g_Handle, "gnssmgt_agps_openWithApnIpType");
  LBSLOGE(GNSS_TEST, "%{public}s agps_open_with_apn addr is %{public}x", GNSSMGT, agps_open_with_apn);

  agps_ril_init= (pAGnssmgt_ril_init)dlsym(g_Handle, "gnssmgt_agps_ril_init");
  LBSLOGE(GNSS_TEST, "%{public}s agps_ril_init addr is %{public}x", GNSSMGT, agps_ril_init);

  agps_ril_ref_location= (pAGnssmgt_ril_ref_location)dlsym(g_Handle, "gnssmgt_agps_ril_setRefLoc");
  LBSLOGE(GNSS_TEST, "%{public}s agps_ril_ref_location addr is %{public}x", GNSSMGT, agps_ril_ref_location);

  agps_ril_set_id= (pAGnssmgt_ril_set_id)dlsym(g_Handle, "gnssmgt_agps_ril_setSetID");
  LBSLOGE(GNSS_TEST, "%{public}s agps_ril_set_id addr is %{public}x", GNSSMGT, agps_ril_set_id);

  agps_ril_net_ability= (pAGnssmgt_ril_update_network_availability)dlsym(g_Handle, "gnssmgt_agps_ril_updateNwAvailability");
  LBSLOGE(GNSS_TEST, "%{public}s agps_ril_net_ability addr is %{public}x", GNSSMGT, agps_ril_net_ability);

  agps_ril_net_state= (pAGnssmgt_ril_update_network_state)dlsym(g_Handle, "gnssmgt_agps_ril_updateNwState");
  LBSLOGE(GNSS_TEST, "%{public}s agps_ril_net_state addr is %{public}x", GNSSMGT, agps_ril_net_state);
  
  if(gps_init==NULL||gps_start==NULL||gps_stop==NULL||gps_cleanup==NULL)
  {
	  LBSLOGE(GNSS, "functions load failed", GNSSMGT);
    return FUNC_NOK;
  }
  
  gps_init(&sGpsCallbacks);
  return ret;
}

int gnss_disable(void) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  gps_cleanup();
  dlclose(g_Handle);
  g_Handle=NULL;
  gps_init=NULL;
  gps_start=NULL;
  gps_stop=NULL;
  gps_cleanup=NULL;
  return 0;
}

int gnss_start(uint32_t type) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  gps_start();
  return 0;
}

int gnss_stop(uint32_t type) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  gps_stop();
  return 0;
}

int gnss_injects_reference_information(int type, GnssReferenceInfo *info) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  return 0;
}

int gnss_set_gnss_config_para(GnssConfigParameter *para) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  return 0;
}

void gnss_remove_auxiliary_data(uint16_t flags) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}

int gnss_inject_extended_ephemeris(char *data, int length) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  return 0;
}

int gnss_get_cached_locations_size() {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  return 0;
}

void gnss_flush_cached_gnss_locations() {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}

const void *get_gnss_module_iface(int iface) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  /*
    AGNSS_MODULE_INTERFACE = 1,
    GNSS_GEOFENCING_MODULE_INTERFACE = 2,
    GNSS_NET_INITIATED_MODULE_INTERFACE = 3,
    GNSS_MEASUREMENT_MODULE_INTERFACE = 4,
  */
  if (iface == AGMI) {
    return get_agnss_interface();
  } 

  return nullptr;
}

/*ohos callback function*/
const GnssVendorInterface *get_gnss_interface() {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  return &g_GVI_;
}

const AgnssModuleInterface *get_agnss_interface() {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  return &g_AGMI_;
}

/* sprd callback function*/
static void location_callback(GpsLocation *location) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  GnssLocation ohos_location = {
      sizeof(GnssLocation),        (uint32_t)location->flags,
      location->latitude,          location->longitude,
      location->altitude,          location->speed,
      location->bearing,           location->accuracy,
      location->veraccuracy,       location->speedaccuracy,
      location->bearaccuracy,      (int64_t)location->timestamp,
      (int64_t)location->timestamp};

  g_GCS_.gnssCallback.locationUpdate(&ohos_location); // GnssLocation* location
}

static void status_callback(GpsStatus *status) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  /* GNSS status unknown. */
  //  GNSS_STATUS_NONE = 0,
  /* GNSS has begun navigating. */
  //  GNSS_STATUS_SESSION_BEGIN = 1,
  /* GNSS has stopped navigating. */
  //  GNSS_STATUS_SESSION_END = 2,
  /* GNSS has powered on but is not navigating. */
  //  GNSS_STATUS_ENGINE_ON = 3,
  /* GNSS is powered off. */
  //  GNSS_STATUS_ENGINE_OFF = 4
  uint16_t ohos_status = status->status;
  g_GCS_.gnssCallback.gnssWorkingStatusUpdate(&ohos_status);
}
static void sv_status_callback(GpsSvStatus *sv_status) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  GnssSatelliteStatus *status;
  status->size = sizeof(GnssSatelliteStatus);
  // status->satellitesNum=sv_status->;
  // g_GCS_.gnssCallback.satelliteStatusUpdate(status);
}

static void nmea_callback(GpsUtcTime timestamp, const char *nmea, int length) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  if(nmea==NULL)
  {
	  LBSLOGE(GNSS, "nmea pointer is null \n", __func__);
	  return ;
  }
  g_GCS_.gnssCallback.nmeaUpdate((int64_t)timestamp, nmea, length);
}

static void set_capabilities_callback(uint32_t capabilities) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}

static void acquire_wakelock_callback() {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}

static void release_wakelock_callback() {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}

static void request_utc_time_callback() {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}

static void gnss_set_system_info_test(const GnssSystemInfo *info) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}

static void gnss_sv_status_callback_test(GnssSvStatus *sv_info) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  GnssSatelliteStatus status;
  status.size = sv_info->size;
  status.satellitesNum=sv_info->numb_svs;
  for (int i = 0; i < sv_info->numb_svs; i++){
    status.satellitesList[i] = {
      sv_info->gnss_sv_list[i].size, sv_info->gnss_sv_list[i].svid,
      sv_info->gnss_sv_list[i].constellation, sv_info->gnss_sv_list[i].c_n0_dbhz,
      sv_info->gnss_sv_list[i].elevation, sv_info->gnss_sv_list[i].zaimuth,
      sv_info->gnss_sv_list[i].carrier_freq, (uint32_t)sv_info->gnss_sv_list[i].flags,
      };
  }

  g_GCS_.gnssCallback.satelliteStatusUpdate(&status);
}

pthread_t create_thread_callback(const char *name, void (*start)(void *),
                                 void *arg) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  pthread_t pid=0;
  int ret=0;
  if(start==NULL)
  {
	  LBSLOGE(GNSS, "start pointer is null \n", __func__);
	  return pid;
  }
  ret=pthread_create(&pid, NULL, (void *(*)(void *))start, arg);
  if(ret!=0)
  {
	  LBSLOGE(GNSS, "crate pthread failure \n", __func__);
	  return pid;
  }
  return pid;
}

void aGnssStatusChange(const AgnssDataConnectionRequest *status) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}

void aGnssgetSetid(uint16_t type) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);

}

void aGnssgetRefLoc(uint32_t type) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
}


AgnssCallbackIfaces g_GACS_;
bool aGnssSetCallback(AgnssCallbackIfaces *callbacks) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  g_GACS_ = *callbacks;
  agps_init(&sAGpsCallbacks);
  agps_ril_init(&sAGpsRilCallbacks);
  agps_setPosMode(GPS_POSITION_MODE_MS_BASED, GPS_POSITION_RECURRENCE_PERIODIC, 1000,1,1);
  return true;
}

static AGpsRefLocationType AGnssRefLocTypeConvert(uint32_t inType)
{
  switch (inType) {
        // case CELLID_TYPE_GSM:
        case 1:
            return AGPS_REF_LOCATION_TYPE_GSM_CELLID;
        // case CELLID_TYPE_UMTS:
        case 2:
            return AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
        // case CELLID_TYPE_LTE:
        case 3:
            return AGPS_REF_LOCATION_TYPE_LTE_CELLID;
        // case CELLID_TYPE_NR:
        case 4:
            return AGPS_REF_LOCATION_TYPE_NR_CELLID;
        // case :
            // return AGPS_REF_LOCATION_TYPE_LTE_CELLID_EX;
        // case :
            // return AGPS_REF_LOCATION_TYPE_MAC
        default:
            LBSLOGE(GNSS_TEST, "%{public}s wrong cellType.", __func__);
    }

    return 0;
}

bool aGnssSetRefLocation(const AgnssReferenceInfo *refLoc) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  AGpsRefLocation sRefLoc;
  sRefLoc.type = AGnssRefLocTypeConvert(refLoc->category);
  switch (sRefLoc.type){
    case AGPS_REF_LOCATION_TYPE_GSM_CELLID:
    case AGPS_REF_LOCATION_TYPE_UMTS_CELLID:
    case AGPS_REF_LOCATION_TYPE_LTE_CELLID:
    case AGPS_REF_LOCATION_TYPE_LTE_CELLID_EX:
    case AGPS_REF_LOCATION_TYPE_NR_CELLID:
      sRefLoc.u.cellID.type = sRefLoc.type;
      sRefLoc.u.cellID.mcc = refLoc->u.cellId.mcc;
      sRefLoc.u.cellID.mnc = refLoc->u.cellId.mnc;
      sRefLoc.u.cellID.lac = refLoc->u.cellId.lac;
      sRefLoc.u.cellID.cid = refLoc->u.cellId.cid;
      sRefLoc.u.cellID.tac = refLoc->u.cellId.tac;
      sRefLoc.u.cellID.pcid = refLoc->u.cellId.pcid;
      break;
    case AGPS_REF_LOCATION_TYPE_MAC:
      break;
  }
  LBSLOGI(GNSS, "%{public}s: cellID.mcc is %u \n", __func__, sRefLoc.u.cellID.mcc);
  LBSLOGI(GNSS, "%{public}s: cellID.mnc is %u \n", __func__, sRefLoc.u.cellID.mnc);
  LBSLOGI(GNSS, "%{public}s: cellID.lac is %u \n", __func__, sRefLoc.u.cellID.lac);
  LBSLOGI(GNSS, "%{public}s: cellID.cid is %u \n", __func__, sRefLoc.u.cellID.cid);
  LBSLOGI(GNSS, "%{public}s: cellID.tac is %u \n", __func__, sRefLoc.u.cellID.tac);
  LBSLOGI(GNSS, "%{public}s: cellID.pcid is %u \n", __func__, sRefLoc.u.cellID.pcid);

  size_t size = sizeof(AGpsRefLocation);
  LBSLOGI(GNSS, "%{public}s: sRefLoc.u.cellID.size is %d \n", __func__, sRefLoc.u.cellID.type);
  agps_ril_ref_location(&sRefLoc, size);
  return true;
}
bool aGnssSetSetid(uint16_t type, const char *setid, size_t len) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  agps_ril_set_id(type, setid);
  return true;
}

bool aGnssSetAgnssServer(uint16_t type, const char *server, size_t len,
                         int32_t port) {
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  agps_set_server(type, server, port);
  return true;
}

static void  agnss_status_callback(AGpsStatus* status){
  AGpsType type = 0;
  int ret;
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  LBSLOGI(GNSS_TEST, "AGPS type:%{public}s status:%{public}d ip:%{public}d\n", 
                      ((status->type == AGPS_TYPE_SUPL) ? "supl" : "c2k"),
                      status->status, status->ipaddr);
  AgnssDataConnectionRequest ohos_status;
  ohos_status.size = status->size;
  ohos_status.agnssCategory = status->type;
  ohos_status.requestCategory = status->status;
  ohos_status.ipaddr = status->ipaddr;

  switch (status->status){
    case GPS_REQUEST_AGPS_DATA_CONN:
      LBSLOGI(GNSS, "%{public}s AGPS connection request.\n", __func__);
      ret = agps_ril_net_state(1, 0, 0, NULL);
      LBSLOGI(GNSS, "%{public}s agps_ril_net_state ret is %{public}d .\n", __func__, ret);
      if ( ret != 0 ){
        LBSLOGI(GNSS, "%{public}s AGPS update state failed.\n", __func__);
        return;
      }
      ret = agps_open_conn(APN_NAME);
      LBSLOGI(GNSS, "%{public}s agps_open_conn ret is %{public}d .\n", __func__, ret);
      if ( ret != 0 ){
        LBSLOGI(GNSS, "%{public}s AGPS open conn failed.\n", __func__);
        return;
      }
      break;
    case GPS_RELEASE_AGPS_DATA_CONN:
      LBSLOGI(GNSS, "%{public}s AGPS release conn failed.\n", __func__);
      break;
    case GPS_AGPS_DATA_CONNECTED:
      LBSLOGI(GNSS, "%{public}s AGPS data connection initiated.\n", __func__);
      break;
    case GPS_AGPS_DATA_CONN_DONE:
      LBSLOGI(GNSS, "%{public}s AGPS data connection completed.\n", __func__);
      break;
    case GPS_AGPS_DATA_CONN_FAILED:
      LBSLOGI(GNSS, "%{public}s AGPS data connection failed.\n", __func__);
      break;
    default:
      LBSLOGI(GNSS, "%{public}s AGPS unknown status.\n", __func__);
  }

  LBSLOGI(GNSS, "%{public}s: entering function requestSetupDataLink.\n", __func__);

  g_GACS_.requestSetupDataLink(&ohos_status);
  // g_GACS_.requestSetid(ohos_status.agnssType);
  return;
}

 pthread_t agnss_create_thread_cb(const char* name, void (*start)(void *), void* arg){
  LBSLOGI(GNSS, "enter function %{public}s \n", __func__);
  pthread_t pid=0;
  int ret=0;
  if(start==NULL)
  {
	  LBSLOGE(GNSS, "start pointer is null \n", __func__);
	  return pid;
  }
  ret=pthread_create(&pid, NULL, (void *(*)(void *))start, arg);
  if(ret!=0)
  {
	  LBSLOGE(GNSS, "crate pthread failure \n", __func__);
	  return pid;
  }
  return pid;
 }

static void agnss_ril_request_set_id_callback(uint32_t flags)
{
  LBSLOGI(GNSS_TEST, "enter function %{public}s flags:%{public}d\n", __func__, flags);
  uint16_t type = static_cast<uint16_t>(flags);
  g_GACS_.requestSetid(type);
}

static void agnss_ril_request_ref_loc_callback(uint32_t flags)
{
  LBSLOGI(GNSS_TEST, "enter function %{public}s, flags is:%{public}d\n", __func__, flags);
  g_GACS_.requestRefInfo(flags);
}
